package parser;

import io.SqlDb;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.sql.SQLException;
import java.util.Scanner;
import java.util.regex.Pattern;

import main.Initializer;

import org.apache.tools.bzip2.CBZip2InputStream;

import com.ice.tar.InvalidHeaderException;
import com.ice.tar.TarEntry;
import com.ice.tar.TarInputStream;
import com.ice.tar.tar;

import track.*;

/**
 * This is the parser that parses the db-data files
 * 
 * @author Alex
 *
 */
public class DbDataParser {

	private String _folder;

	/**
	 * The constructor that initializes the folder
	 * 
	 * @param folder - Then folder in which all the data should be
	 * 
	 * @pre All the files are db-data files. Meaning, no other files should be in the folder
	 */
	public DbDataParser(String folder)
	{
		this._folder = folder;
	}
	
	/**
	 * Set the new folder
	 * 
	 * @param folder
	 */
	public void setFolder(String folder)
	{
		this._folder = folder;
	}
	
	/**
	 * Get the existing folder
	 * @return
	 */
	public String getFolder()
	{
		return _folder;
	}
	
	/************************************
	 * All the parser model
	 ************************************ 
	 */
	public void parse()
	{
		File root = new File(this._folder);
		CBZip2InputStream bz2 = null;
		TarInputStream tin = null;
        TarEntry tarEntry = null;
		InputStream in = null;
		Genre genre = null;
		String genreName = "";
		boolean isGenreSet = false;
        BufferedReader reader = null;
        
		int countForClose = 0;
        
		String currLine = "";
		String exitCode = "EXTD=";

		try {
			in = new FileInputStream(root);
			in.skip(2);
	        bz2 = new CBZip2InputStream(in);
	        tin = new TarInputStream(bz2);
	        tarEntry = tin.getNextEntry();
	        reader = new BufferedReader(new InputStreamReader(tin));
	        
//			buffReader = new BufferedReader(new InputStreamReader(tin));
//			buffReader.readLine();
		} catch (FileNotFoundException e1) {
			return;
		} catch (IOException e) {
			e.printStackTrace();
		}
		//Check all the folder. Generally those are the folders of the different genres 		
		while(tarEntry != null) {
			
			//Clean once in a few times the connection
			if(countForClose++ == 1000) {
				try {
					countForClose = 0;
					SqlDb.getConnection().close();
					Initializer init = new Initializer();
					init.init();
				} catch (SQLException e) {
					//Do nothing
				}
				
			}
			
			if(tarEntry.isDirectory()) {
				genreName = tarEntry.getName();
				genreName = genreName.substring(0, genreName.length() - 1);
				isGenreSet = true;
				genre = TracksFuncs.getGenreFactory(genreName);
				
				//if the genre wasn't created
				if(genre == null)
					continue;
			}
			else {
				//parameters for the artist
				Artist artist = null;
				boolean isArtistSet = false, isSingleArtist = false;
				String artistName = "";
				
				//parameters for the disc
				Disc disc = null;
				boolean isDiscSet = false, isDiscCreated = false;
				String discName = "";
				int discNum = 0;
				
				//parameters for the sub genre
				SubGenre subGenre = null;
				boolean isSubGenreSet = false;
				String subGenreName = "";
				
				String discNumString = "";
				
				currLine = "";

				try {
					currLine = reader.readLine();
				} catch (IOException e1) {
					break;
				}
				while(! (currLine == null ? exitCode : currLine).equals("EXTD=")) {
					try {
						String attributeName, attributeValue, trackName;					
						
						//If the line starts with a # then it's a comment and should be ignored
						if(currLine.charAt(0) == '#') {
							currLine = reader.readLine();
							continue;	
						}
	
						//Check that this is an English text
						Pattern _notAsciiPattern = Pattern.compile("[^\\p{ASCII}]");
						if(_notAsciiPattern.matcher(currLine).find())
							break;
	
						attributeName = getAttributeNameFromLine(currLine);
						//The value of the attribute is after the name and the '='
						attributeValue = currLine.substring(attributeName.length() + 1);
	
						if(attributeName.equals("DISCID")) {
							discNumString = attributeValue;	
							discNum = Integer.parseInt(attributeValue , 16);
							isDiscSet = true;
						}
						else if(attributeName.equals("DTITLE")) {
								if(! isArtistSet) {
									artistName = getArtistNameFromLine(attributeValue);
								
									if((! artistName.equals("")) && (! artistName.contains("Various"))) {
										isArtistSet = true;
										artist = TracksFuncs.getArtistFactory(artistName);
										isSingleArtist = true;
									}
								}
								
								discName = attributeValue.substring(artistName == "" ? 0 : artistName.length() + 3);
								isDiscSet = true;
						}
						else if(attributeName.equals("DGENRE")) {
							if(! isGenreSet)
								break;
								
							subGenreName = attributeValue;
							isSubGenreSet = true;
							subGenre = TracksFuncs.getSubGenreFactory(subGenreName, genre);
						}
						else if(attributeName.startsWith("TTITLE")) {
							//Set the artist if there are different artists for the disc
							if(! isSingleArtist) {
/*-----*/						artistName = getArtistNameFromLine(attributeValue);
								artist = TracksFuncs.getArtistFactory(artistName);
								isArtistSet = true;
							}
							
							if(isDiscSet && (! isDiscCreated) && isSubGenreSet) {
								disc = TracksFuncs.createDiscFactory(discNum, discName, subGenre);
								isDiscCreated = true;
							}
//							else {
//								break;
//							}
							
							//If the artist is still not set, not to write that track
							if((! isArtistSet) || (! isDiscCreated) || (! isSubGenreSet))
								break;
					
							//Set the name of the track
							trackName = getTrackName(artistName, attributeValue, isSingleArtist);
							
							//Create the track
							Track track = TracksFuncs.createTrackFactory(trackName, artist, disc, getNumOfTrack(attributeName));
							
							//System.out.println(discNumString);
							//System.out.println(track);
						}
						
						currLine = reader.readLine();
					}
					catch(Exception e) {
						break;
					}
				}			
			}
			
			try {
				tarEntry = tin.getNextEntry();
				currLine = reader.readLine();
			} catch (IOException e) {
				break;
			}
		}
	}

    /**
     * This function gets the name of the attribute in the line
     * 
     * @param line
     */
    private String getAttributeNameFromLine(String line)
    {
    	for(int i = 0; i < line.length(); i++) {
    		if(line.charAt(i) == '=')
    			return line.substring(0, i);
    	}
    	
    	return "";
    }
    
    /**
     * The line in the file after the '='
     * 
     * @param line
     * @return
     */
    private String getArtistNameFromLine(String restOfLine)
    {
    	for(int i = 0; i < restOfLine.length(); i++) {
    		if((restOfLine.charAt(i) == '/') || (restOfLine.charAt(i) == '-'))
    			return restOfLine.substring(0, i - 1);
    	}
    	
    	return "";
    }
    
    /**
     * returns the number of the track as it's written in the db-free files
     * 
     * @param attributeName - The current line
     * 
     * @return int
     */
    private int getNumOfTrack(String attributeName)
    {
    	return Integer.valueOf(attributeName.substring(6)); // 6 = "TTITLE".length()
    }
    
    /**
     * Get the name of the song
     * 
     * @param attributeValue
     * @param attributeValue2 
     * @param isSingleArtist
     * @return
     */
	private String getTrackName(String artistName, String attributeValue, boolean isSingleArtist) 
	{
		if(isSingleArtist)
			return attributeValue;
		else {
			return attributeValue.substring(artistName.length() + 3);
		}
	}
}
